package org.codeanalyzer;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.codeanalyzer.model.ApiLibrary;
import org.codeanalyzer.report.UsageReportBuilder;
import org.codeanalyzer.util.Utils;
import org.codeanalyzer.xml.XmlService;

import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;

public class UsageAnalyzer
{

    public static void main(String[] args)
    {
        try
        {
            if (args.length < 2)
            {
                System.out.println("Usage: java " + UsageAnalyzer.class.getName()
                    + " src_dir xml_file");
                System.out.println("          src_dir: the direcotry containing source code to analyze");
                System.out.println("          xml_file: The xml file generated by ApiAnalyzer");
                System.exit(0);
            }

            File sourceDir = new File(args[0]);
            if (sourceDir == null || !sourceDir.isDirectory())
            {
                System.err.println("The directory " + args[0] + "does not exist");
                System.exit(0);
            }
            File xmlFile = new File(args[1]);
            if (xmlFile == null || !xmlFile.isFile())
            {
                System.err.println("The xml file " + args[0] + "does not exist");
                System.exit(0);
            }

            XmlService xmlService = new XmlService();
            ApiLibrary apiLibrary = xmlService.load(xmlFile);
            System.out.println("Loaded API library: " + apiLibrary.getName());

            List<File> sourceFiles = Utils.listJavaFilesRecursively(sourceDir);

            UsageAnalyzer usageAnalyzer = new UsageAnalyzer();
            usageAnalyzer.analyze(sourceFiles, apiLibrary);

        }
        catch(Throwable t)
        {
            t.printStackTrace();
            System.exit(1);
        }
    }

    public void analyzeFromZip(String zipFileName, ApiLibrary apiLibrary) throws IOException
    {
        List<File> files = Utils.extractJavaFilesFromZip(zipFileName);
        System.out.println("Extracted " + files.size() + " source files");

        analyze(files, apiLibrary);
    }

    public void analyze(List<File> sourceFiles, ApiLibrary apiLibrary) throws IOException
    {
        long start = System.currentTimeMillis();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

        try
        {
            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFiles);
            JavacTask task = (JavacTask)compiler.getTask(null, fileManager, null, null, null, compilationUnits);
            UsageAnalyzerTreeVisitor visitor = new UsageAnalyzerTreeVisitor(apiLibrary);

            // Implemented using ideas from the link:
            // http://stackoverflow.com/questions/1066555/discover-the-class-of-a-methodinvocation-in-the-annotation-processor-for-java

            Iterable<? extends CompilationUnitTree> asts = task.parse();
            task.analyze();

            for(CompilationUnitTree ast : asts)
            {
                visitor.compilationUnit = ast;
                visitor.scan(ast, null);
            }
        }
        finally
        {
            fileManager.close();
        }

        long end = System.currentTimeMillis() - start;
        UsageReportBuilder reportBuilder = UsageReportBuilder.getInstance();
        reportBuilder.printReport(apiLibrary, end, sourceFiles.size());
    }
}
